/***
 * * PROJECT
 * *    ATaG
 * * VERSION
 * *    $Revision: 1.4 $
 * * DATE
 * *    $Date: 2008/04/19 09:15:22 $
 * * LOG
 * *    $Log: MIPMain.java,v $
 * *    Revision 1.4  2008/04/19 09:15:22  animesh
 * *    multipath MIPs done
 * *
 * *    Revision 1.3  2008/01/03 20:07:11  animesh
 * *    no message
 * *
 * *    Revision 1.2  2007/11/26 00:04:33  animesh
 * *    added INFINITY as a parameter for greedy task assigner
 * *
 * *    Revision 1.1  2007/11/20 22:44:56  animesh
 * *    no message
 * *
 ***/

package atag.compiler;

import atag.compiler.powerassigner.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Vector;

import atag.common.ATaGDataItemDeclaration;
import atag.common.ATaGProgram;
import atag.common.ATaGTaskDeclaration;
import atag.common.NetworkTopology;
import atag.common.ScopedChannel;
import atag.runtime.NodeInfo;
import atag.runtime.ln.LNScope;
import atag.runtime.ln.estimation.LNSimpleEstimator;
import atag.runtime.ln.estimation.LNStaticHelper;

/**
 *
 * @author Animesh
 */
public class MIPMain {
    
    /**
     * constants to represent file paths can maybe replaced with arguments that
     * are passed when the program runs
     */
    
    private static String absoluteBase = ".";
    
    private static String autoGenAppDir = absoluteBase
            + "\\AutoGenerated\\Application";
    
    private static String autoGenRoot = absoluteBase + "\\AutoGenerated";
    
    private static String configFileDestDir = absoluteBase + "\\config";
    
    // private static String networkFileBase = absoluteBase +
    // "\\AutoGenerated\\Network\\";
    private static String networkFileBase = absoluteBase + "\\network\\";
    
    private static String runNodesBatchFilePath = absoluteBase
            + "\\DART-class\\runNodes.bat";
    
    private static String runGUIBatchFilePath = absoluteBase
            + "\\GUI-class\\runGUI.bat";
    
    // TODO - get these parameters as inputs
    private static String codeBaseDirPath = "";
    private static String deploymentBaseDirPath = "";
    
    
    
    /********* optimization styles****/
    private static final int OPTIMIZATION_MIP = 0;
    private static final int OPTIMIZATION_GREEDY = 1;
    private static final int OPTIMIZATION_MIP_MIN_TOTAL_COST = 2;
    private static final int OPTIMIZATION_GREEDY_MIN_TOTAL_COST = 3;
    private static final int OPTIMIZATION_MULTIPATH_MIP = 4;
    private static final int OPTIMIZATION_MULTIPATH_GREEDY = 5;
    private static final int OPTIMIZATION_MULTIPATH_MIP_MIN_TOTAL_COST = 6;
    private static final int OPTIMIZATION_MULTIPATH_GREEDY_MIN_TOTAL_COST = 7;
            
    
    private static final double INFINITY = 10e7; //TODO - change later.
    
    
    /** Creates a new instance of Main */
    public MIPMain() {
    }
    
    /**
     * @param args
     *            the command line arguments Essentially take the filename from
     *            command prompt and feed to the parser class. Then generate all
     *            the files.
     */
    public static void main(String[] args) {
        
        int optimizationStyle = 0;
        
        
        boolean configFileFlag = false;
        boolean codeTemplateFlag = false;
        
        int randomSeed;
        
        byte optimizationFlag = 0x0;
        
        if (args.length != 3) {
            System.err.println("Usage: java appname atagfilename topologyfilename "
                    + "[MultiPath]{MIP|greedy|greedyTotalCost|MIPTotalCost}");
            System.exit(1);
        }
        
        ATaGProgram aProgram = ATaGParser.parseATaGProgram(args[0]);
        
        aProgram.sanitize();
        
        // read the topology file into a Network Topology
        NetworkTopology nTopo = new NetworkTopology();
        nTopo.readTopologyFile(args[1]);
        // possibly parse the args and set paths
        // TODO
        
        // set optimization style
        
        if (args[2].equalsIgnoreCase("MIP")) {
            optimizationStyle = OPTIMIZATION_MIP;
        } else if (args[2].equalsIgnoreCase("greedy")) {
            optimizationStyle = OPTIMIZATION_GREEDY;
        } else if (args[2].equalsIgnoreCase("greedyTotalCost")) {
            optimizationStyle = OPTIMIZATION_GREEDY_MIN_TOTAL_COST;
        } else if (args[2].equalsIgnoreCase("MIPTotalCost")) {
            optimizationStyle = OPTIMIZATION_MIP_MIN_TOTAL_COST;
        } else if (args[2].equalsIgnoreCase("MultiPathMIP")) {
            optimizationStyle = OPTIMIZATION_MULTIPATH_MIP;
        } else if (args[2].equalsIgnoreCase("MultiPathgreedy")) {
            optimizationStyle = OPTIMIZATION_MULTIPATH_GREEDY;
        } else if (args[2].equalsIgnoreCase("MultiPathgreedyTotalCost")) {
            optimizationStyle = OPTIMIZATION_MULTIPATH_GREEDY_MIN_TOTAL_COST;
        } else if (args[2].equalsIgnoreCase("MultiPathMIPTotalCost")) {
            optimizationStyle = OPTIMIZATION_MULTIPATH_MIP_MIN_TOTAL_COST;
        } else {
            System.out.println("Invalid task " + args[2] + " supplied");
            System.exit(-8);
        }
        
        giveNewAssignments(aProgram, nTopo, optimizationStyle);
        
    }
    
    
    
    private double[] EnergyEstimate(NetworkTopology nTopo) {
        int dummyID = 0;
        LNScope[] destinationScopes = new LNScope[2];
        
        Estimator m_Estimator = new DummyEstimator(nTopo);
        
        ArrayList communications = new ArrayList();
        Vector newElement = new Vector();
        newElement.add(new Integer(dummyID));
        newElement.add(destinationScopes);
        
        return m_Estimator.getCostEstimate(communications);
    }
    
    
    /**
     * Compiles the program.
     * @deprecated
     */
    private static void compileProgram(ATaGProgram aProgram,
            NetworkTopology nTopo, boolean codeTemplateFlag,
            boolean configFileFlag, byte optimizationFlag, int randomSeed,
            String dartRoot) {
        
        // first.. generate the Task and DataItem Templates, if needed
        // [remember to print a message for the user when the files are created]
        if (codeTemplateFlag){
//            createCodeTemplates(aProgram, dartRoot);
        }
        
        
        
        // then, if needed, Generate an ITaG from an ATaG
        if(configFileFlag){
            ITaGProgram equivITaG = new ITaGProgram(aProgram, nTopo);
            
            ComposedATaG caProgram = new ComposedATaG(aProgram);
            
            
            
            //the line below will later get a switch/case when we have many task assigners
            TaskAssigner tAssigner = null;
            switch(optimizationFlag){
                case 0x01:
                    tAssigner = new RandomTaskAssigner();
                    break;
                case 0x02:
                    tAssigner = new StickyTaskAssigner();
                    break;
                case 0x04:
                    tAssigner = new CentroidTaskAssigner();
                    break;
                default:
                    System.err.println("Invalid optimization flag ID " + optimizationFlag);
                    System.exit(-8);
            }
            
            
            tAssigner.assignTasks(aProgram, equivITaG, caProgram, nTopo, randomSeed);
            
            LNSimpleEstimator estimator = new LNSimpleEstimator(nTopo);
            
            
            LinkedList<CommPattern> commPatterns = getCommunicationPatterns(
                    aProgram, nTopo);
            
            
            double[] costEstimates = estimator.getCostEstimate(commPatterns);
            
            printCostEstimates(nTopo, costEstimates);
            
        }
        
    }
    
    /**
     * @param aProgram
     *            We assume that the tasks in this already know where they are
     *            assigned
     * @param nTopo
     *            We assume that the nodes in this network topology already know
     *            what nodes are assigned to them
     * @returns A linked list of communication patterns
     */
    private static LinkedList<CommPattern> getCommunicationPatterns(
            ATaGProgram aProgram, NetworkTopology nTopo) {
        LinkedList<CommPattern> retList = new LinkedList<CommPattern>();
        
        for (ATaGTaskDeclaration t : aProgram.getTaskList()) {
            if (t.getFiringRule()[0] == ATaGTaskDeclaration.FIRING_PERIODIC) {
                for (Integer assignedNodeID : t.getAssignedTo()) {
                    // for each assigned node, for each periodic task, trace
                    // till end
                    addEdge(nTopo, assignedNodeID.intValue(), aProgram, t,
                            retList);
                    // TODO this is where you can multiply the cost by the
                    // number of times
                    // the task will fire in the "sample period"
                }
            }
        }
        
        return retList;
    }
    
    private static void addEdge(NetworkTopology nTopo, int nodeID,
            ATaGProgram aProgram, ATaGTaskDeclaration t, LinkedList retList) {
        
        NodeInfo tempN = nTopo.getNodeInfoByID(nodeID);
        
        // for each data item produced by the task
        for (ATaGDataItemDeclaration d : aProgram.getOutDataListOfTask(t
                .getID())) {
            
            ArrayList<ATaGTaskDeclaration> targetTasks = aProgram
                    .getOutTasksListOfData(d.getID());
            // int numOutChannels = targetTasks.size();
            // for each task "consuming that data item"
            for (ATaGTaskDeclaration targetT : targetTasks) {
                LNScope tempScope = aProgram.getLNScopeForData(t.getID(), d
                        .getID(), tempN, targetT);
                // add that scope to the list
                retList.add(new CommPattern(nodeID, tempScope));
                // get list of nodes belonging to that scope
                LinkedList<Integer> targetNodeIDs = LNStaticHelper
                        .determineMatchingNodes(nTopo, tempScope);
                // for each such node, if it has targetT, recurse
                for (Integer tnID : targetNodeIDs) {
                    if (nTopo.getNodeInfoByID(tnID.intValue()).isTaskAssigned(
                            targetT.getID())) {
                        addEdge(nTopo, tnID.intValue(), aProgram, targetT,
                                retList);
                    }
                }
            }
        }
        return;
    }
    
    private static void printCostEstimates(NetworkTopology nTopo, double[] costEstimates) {
        int maxNodeID = -1;
        double maxNodeCost = 0;
        double totalCost = 0;
        
        for (int i = 0; i< nTopo.getNumberOfNodes(); i++){
            totalCost = totalCost + costEstimates[i];
            if (costEstimates[i] > maxNodeCost){
                maxNodeCost = costEstimates[i];
                maxNodeID = i;
            }
        }
        
        //Now do the printing
        
        System.out.println("#----" + nTopo.getNumberOfNodes() + "nodes----------------");
        System.out.println("#numNodes xrange yrange maxnodeID maxCost totalCost");
        System.out.println(nTopo.getNumberOfNodes() + " " +
                nTopo.getXRange() + " " + nTopo.getYRange() + " "
                + maxNodeID + " " + maxNodeCost + " " + totalCost);
        for (int i = 0; i< nTopo.getNumberOfNodes(); i++){
            // x y cost
            NodeInfo thisNode = nTopo.getNodeInfoByID(i);
            System.out.println(thisNode.getMyLocation().getX() + " "  +
                    thisNode.getMyLocation().getY() + " " + costEstimates[i]);
        }
        System.out.println("#-----------------------------");
    }

    /**
     * @returns R[][][] Energy spent by each for node per message between
     * another pair of nodes
     * @param NTopo Network Description
     */
    public static double[][][] returnRoutingMatrix(NetworkTopology NTopo){
        int n = NTopo.getNumberOfNodes();//total nodes
        
        //first create the adjacency matrix
        double[][] s = new double[n][n];
        
        
       
        //initialize s
        for(int i=0;i<n;i++){
            for(int j=i;j<n;j++){
                //upper triangular matrix is traversed
                s[i][j] = GraphUtils.INFINITY;
                s[j][i] = GraphUtils.INFINITY;
            }
        }
        
        //now update with actual costs
        for(int i=0; i<n;i++){
            s[i][i]=0;
            for(NodeInfo nj:NTopo.getNeighborsOf(i)){
                int j = nj.getMyId();
                //TODO - perhaps look at a property of i and j and set costs
                s[i][j] = 1;
                s[j][i] = 1;
            }
        }
        
        return GraphUtils.returnRoutingMatrix(s, n);
        
    }
    
    /**
     * @returns R[][][][] Energy spent by each for node per message between
     * another pair of nodes for each path
     * @param NTopo Network Description
     * @param phi number of paths
     */
    public static double[][][][] returnMultiPathRoutingMatrix(NetworkTopology NTopo, 
            int phi){
        int n = NTopo.getNumberOfNodes();//total nodes
        
        //first create the adjacency matrix
        double[][] s = new double[n][n];
        
        
       
        //initialize s
        for(int i=0;i<n;i++){
            for(int j=i;j<n;j++){
                //upper triangular matrix is traversed
                s[i][j] = GraphUtils.INFINITY;
                s[j][i] = GraphUtils.INFINITY;
            }
        }
        
        //now update with actual costs
        for(int i=0; i<n;i++){
            s[i][i]=0;
            for(NodeInfo nj:NTopo.getNeighborsOf(i)){
                int j = nj.getMyId();
                //TODO - perhaps look at a property of i and j and set costs
                s[i][j] = 1;
                s[j][i] = 1;
            }
        }
        
        return GraphUtils.returnMultiPathRoutingMatrix(s, n, phi);
        
    }

    
    /**
     * @returns nodes in a BFS order -- assuming it is a DAG
     * @param AProgram ATaG Program
     * @param m number of nodes
     */
    public static ATaGTaskDeclaration[] giveBFSOrder(ATaGProgram aProgram){
        //array to be returned
        int m = aProgram.numTasks();
        ATaGTaskDeclaration[] bfs = new ATaGTaskDeclaration[m];
        
        //initialize queue to empty
        //LinkedList implements Queue
        Queue<ATaGTaskDeclaration> tempQ = new LinkedList();
        
        //flag arrays
        boolean[] inTempQ = new boolean[m];
        boolean[] inBFS = new boolean[m];
        
        //initializing the flag arrays
        for(int i=0; i<m; i++){
            inTempQ[i] = false;
            inBFS[i] = false;
        }
        
        //init the queue with the tasks with no feeders
        for(int i=0; i<m; i++){
            ATaGTaskDeclaration at = aProgram.getTaskList().get(i);
            int instType = at.getLocation()[0];
            int instParam = at.getLocation()[1];
            int atID = at.getID();
            
            if(at.getFiringRule()[0] == ATaGTaskDeclaration.FIRING_PERIODIC){
                tempQ.offer(at);
                inTempQ[i] = true;
            }
        }
        
        int count = 0;
        
        ATaGTaskDeclaration at;
        
        while((at = tempQ.poll()) != null){
            boolean isReady = true;
            for(ATaGTaskDeclaration feederA : aProgram.getFeederTasks(at)){
                if ((inBFS[feederA.getID()] == false)){
                    //there is a feeder who is not in the BFS yet
                    isReady = false;
                    break;
                }
            }//end for
            if(isReady){
                bfs[count++] = at;
                inBFS[at.getID()] = true;
                inTempQ[at.getID()] = false;
                
                
                
                ///////////insert each child node..
                
                for(ATaGDataItemDeclaration testData:
                    aProgram.getOutDataListOfTask(at.getID())){
                        //for each data item produced by this task
                        for (ATaGTaskDeclaration testTask :
                            aProgram.getOutTasksListOfData(testData.getID())){
                                //for each task consuming this data item
                                if(inTempQ[testTask.getID()] == false){
                                    //there is a child task who is not yet in the tempQ
                                    // if it is not inserted yet in the tempQ
                                    // insert it
                                    tempQ.offer(testTask);
                                    inTempQ[testTask.getID()] = true;
                                }
                            }
                    }
            }else{
                //not ready yet.
                //reinsert the element
                tempQ.offer(at);
            }//end if
        }//end while
        
        if(count != m){
            System.out.println("There seems to be an error... not" +
                    " all elements are in the BFS");
            System.exit(3);
        }
        
        return bfs;
    }
    
    
    /**
     * computes R[][][], T[][], s[][], f[][], and more
     * @param aProgram -- the ATaG program
     * @param NTopo -- the network description
     * @param optimizationStyle -- optimization technique.
     */
    private static void giveNewAssignments(ATaGProgram aProgram,
            NetworkTopology nTopo, int optimizationStyle){
        
        int n = nTopo.getNumberOfNodes();
        
        //count the number of tasks to get m
        int m = 0;
        for (ATaGTaskDeclaration at : aProgram.getTaskList()) {
            int instType = at.getLocation()[0];
            int instParam = at.getLocation()[1];
            int atID = at.getID();
            
            switch (instType) {
                case ATaGTaskDeclaration.INSTANTIATION_ONE_ON_NODE_ID:
                    m++;
                    break;
                case ATaGTaskDeclaration.INSTANTIATION_NODES_PER_INSTANCE:
                    // instantiate on one of each
                    if (at.getNeededAttribute() != null) {
                        // find all nodes with that attribute, and assign this task
                        // on them
                        m = m + nTopo.getNodesWithAttribute(at
                                .getNeededAttribute()).size();
                    } else {
                        // no needed Attribute. Assign on all.
                        m = m+ nTopo.getNumberOfNodes();
                    }
                    break;
                case ATaGTaskDeclaration.INSTANTIATION_PARTITION_PER_INSTANCE:
                    // assume that regionIDs are from 0 to k-1 only
                    // that means no missing regionIDs
                    m = m+ nTopo.numberOfRegions(at.getMyRegionName());
                    break;
                case ATaGTaskDeclaration.INSTANTIATION_ONE_ANYWHERE:
                    int destinationNode;
                    m = m + 1;
                    break;
                case ATaGTaskDeclaration.INSTANTIATION_NODES_PER_INSTANCE_FRACTIONAL:
                    // do nothing.. allow this to run into default. Fix later
                default:
                    System.err.println("Illegal instantiation type "
                            + at.getInstType() + " for task" + at.getName());
                    System.exit(-5);
            }
        }
        
        //m has been found. Now fill T[][]
        int numberOfAbstractTasks = aProgram.getTaskList().size();
        
        double[][] T = new double[m][n];
        int[] instanceCount = new int[numberOfAbstractTasks];
        int[] instanceBegin = new int[numberOfAbstractTasks];
        
        int taskCount = 0;
        //initialize T
        for(int i=0; i<m; i++)
            for(int k=0;k<n;k++)
                T[i][k] = INFINITY;
        
        
        for (ATaGTaskDeclaration at : aProgram.getTaskList()) {
            int instType = at.getLocation()[0];
            int instParam = at.getLocation()[1];
            int atID = at.getID();
            
            switch (instType) {
                case ATaGTaskDeclaration.INSTANTIATION_ONE_ON_NODE_ID:
                    //assign here
                    instanceCount[atID] = 1; //there is one copy of task atID
                    instanceBegin[atID] = taskCount;//and its physical ID is taskcount
                    T[taskCount++][instParam] = 0;//assigning here is free, so 0
                    break;
                case ATaGTaskDeclaration.INSTANTIATION_NODES_PER_INSTANCE:
                    // instantiate on one of each
                    if (at.getNeededAttribute() != null) {
                        // find all nodes with that attribute, and assign this task
                        // on them
                        instanceCount[atID] = nTopo.getNodesWithAttribute(
                                at.getNeededAttribute()).size(); //one copy of at for each
                        instanceBegin[atID] = taskCount;//starting at taskCount
                        for(NodeInfo tempN: nTopo.getNodesWithAttribute(at
                                .getNeededAttribute())){
                            //assign different copies of at each node
                            T[taskCount++][tempN.getMyId()] = 0;
                        }
                    } else {
                        // no needed Attribute. Assign on all.
                        instanceCount[atID] = nTopo.getNumberOfNodes(); //one copy of at for each
                        instanceBegin[atID] = taskCount;//starting at taskCount
                        for(NodeInfo tempN: nTopo.getNodes()){
                            //assign different copies of at each node
                            T[taskCount++][tempN.getMyId()] = 0;
                        }
                    }
                    break;
                case ATaGTaskDeclaration.INSTANTIATION_PARTITION_PER_INSTANCE:
                    // assume that regionIDs are from 0 to k-1 only
                    // that means no missing regionIDs
                    int totalRegions = nTopo.numberOfRegions(at
                            .getMyRegionName());
                    
                    //instParam copies of at for each region
                    instanceCount[atID] = totalRegions*instParam;
                    instanceBegin[atID] = taskCount;//starting at taskCount
                    
                    for (int regionID = 0; regionID < totalRegions; regionID++) {
                        //for each copy of this task [in case it is like
                        //"2 per floor""
                        for(int i=0;i<instParam;i++){
                            //for each region
                            for(NodeInfo tempN: nTopo.getNodesInRegion(at.getMyRegionName(),
                                    regionID)){
                                //task "taskCount" can go on each node
                                T[taskCount][tempN.getMyId()] = 0;
                            }
                            taskCount++; //next region/copy.. next task
                        }
                    }
                    break;
                case ATaGTaskDeclaration.INSTANTIATION_ONE_ANYWHERE:
                    int destinationNode;
                    // completely random assignment -- random selects a number
                    // in [0,1)
                    instanceCount[atID] = 1; //there is one copy of task atID
                    instanceBegin[atID] = taskCount;//and its physical ID is taskcount
                    for(NodeInfo tempN: nTopo.getNodes()){
                        //this task can go on any node
                        T[taskCount][tempN.getMyId()] = 0;
                    }
                    taskCount++;
                    break;
                case ATaGTaskDeclaration.INSTANTIATION_NODES_PER_INSTANCE_FRACTIONAL:
                    // do nothing.. allow this to run into default. Fix later
                default:
                    System.err.println("Illegal instantiation type "
                            + at.getInstType() + " for task" + at.getName());
                    System.exit(-5);
            }
        }
        
        
        //now T[][] is set. Set s[][]
        
        double[][] s = new double[m][m];
        for(int i=0;i<m;i++)
            for(int j=0;j<m;j++)
                s[i][j] = 0;
        
        for (ATaGTaskDeclaration at : aProgram.getTaskList()) {
            int instType = at.getLocation()[0];
            int instParam = at.getLocation()[1];
            int atID = at.getID();
            for(ATaGTaskDeclaration ft: aProgram.getFeederTasks(at)){
                for(int i=instanceBegin[ft.getID()];
                i<instanceBegin[ft.getID()] + instanceCount[ft.getID()]; i++){
                    for(int x=0;x<n;x++){
                        if(T[i][x] == 0){
                            //if a copy of i can be on this node
                            NodeInfo NodeX = nTopo.getNodeInfoByID(x);
                            for(int j=instanceBegin[at.getID()];
                            j<instanceBegin[at.getID()] + instanceCount[at.getID()]; j++){
                                for(NodeInfo NodeY: nodesWithDependantTasks(ft, at, nTopo,
                                        NodeX, aProgram)){
                                    if(T[j][NodeY.getMyId()] == 0){
                                        //if this node has a copy of j, link i and j
                                        s[i][j] = 1;
                                        break; //break out of the nodeY, and onto the next j
                                    }
                                }//for NodeY
                            }//for j
                        }//if
                    }//for x
                }//for i
            }
        }
        
        //s[][] done.. now for f[]
        double[] f = new double[m];
        int[] tasksInBFS = GraphUtils.giveBFSOrder(s,m);
        
        for(int count=0; count<m; count++){
            int i = tasksInBFS[count];
            ATaGTaskDeclaration thisTask = null;
            //find that at that i is a copy of
            for(ATaGTaskDeclaration at: aProgram.getTaskList()){
                if ((i >= instanceBegin[at.getID()])&&
                        (i<instanceBegin[at.getID()] + instanceCount[at.getID()])){
                    // i is between the limits of the IDs of the copies of at
                    thisTask = at;
                    break;
                }
            }
            
            //depending on at, find the f[]
            switch(thisTask.getFiringRule()[0]){
                case ATaGTaskDeclaration.FIRING_PERIODIC:
                    //TODO using period as frequency... fix this!
                    f[i] = thisTask.getFiringRule()[1];
                    break;
                case ATaGTaskDeclaration.FIRING_ANYDATA:
                    double totalrate = 0;
                    for(int feederID=0;feederID<m;feederID++){
                        if (s[feederID][i] != 0){
                            //this is a feeder indeed!
                            totalrate = totalrate + f[feederID];
                        }
                    }
                    f[i] = totalrate;
                    break;
                case ATaGTaskDeclaration.FIRING_ALLDATA:
                    double minrate = INFINITY;
                    for(int feederID=0;feederID<m;feederID++){
                        if (s[feederID][i] != 0){
                            //this is a feeder indeed!
                            if (f[feederID] < minrate)
                                minrate = f[feederID];
                        }
                    }
                    f[i] = minrate;
                    break;
            }
            
        }
        //all variable set.. now get to work..
        
        double[][][] R;
        double[][][][] MR;
        int numPaths = 3; //TODO this needs to be changed, or an argument.

        
        double[] e0 = new double[n];
        switch(optimizationStyle){
            case OPTIMIZATION_MIP:
                R = returnRoutingMatrix(nTopo);
                LPCreator.printMinMaxCostLPtoStream(m,n,T,R,f,s,e0,System.out, INFINITY);
                break;
            case OPTIMIZATION_MIP_MIN_TOTAL_COST:
                R = returnRoutingMatrix(nTopo);
                LPCreator.printMinTotalCostLPtoStream(m,n,T,R,f,s,e0,System.out, INFINITY);
                break;
            case OPTIMIZATION_GREEDY:
                R = returnRoutingMatrix(nTopo);
                int[][] X1 = SimpleGreedyAssigner.returnAssignmentMinMaxCost(m,n,T,R,f,s,e0, INFINITY);
                MeasuringUtils.reportStats(m,n,T,R,f,s,e0,X1);
                break;
            case OPTIMIZATION_GREEDY_MIN_TOTAL_COST:
                R = returnRoutingMatrix(nTopo);
                int[][] X2 = SimpleGreedyAssigner.returnAssignmentMinTotalCost(m,n,T,R,f,s,e0, INFINITY);
                MeasuringUtils.reportStats(m,n,T,R,f,s,e0,X2);
                break;
            case OPTIMIZATION_MULTIPATH_MIP:
                MR = returnMultiPathRoutingMatrix(nTopo, numPaths);
                LPCreator.printMultiPathMinMaxCostLPtoStream(m,n,numPaths,T,MR,f,s,e0,System.out, INFINITY);
                break;
            case OPTIMIZATION_MULTIPATH_MIP_MIN_TOTAL_COST:
                MR = returnMultiPathRoutingMatrix(nTopo, numPaths);
                LPCreator.printMultiPathMinTotalCostLPtoStream(m,n,numPaths,T,MR,f,s,e0,System.out, INFINITY);
                break;
            case OPTIMIZATION_MULTIPATH_GREEDY:
                MR = returnMultiPathRoutingMatrix(nTopo, numPaths);
                TaskAndRouteMapping TRM1 = 
                        SimpleGreedyAssigner.returnAssignmentMultiPathMinMaxCost(m,n,numPaths,T,MR,f,s,e0, INFINITY);
                MeasuringUtils.reportMultiPathStats(m,n,T,MR,f,s,e0,TRM1.getTaskMapping(),TRM1.getRouteMapping());
                break;
            case OPTIMIZATION_MULTIPATH_GREEDY_MIN_TOTAL_COST:
                MR = returnMultiPathRoutingMatrix(nTopo, numPaths);
                TaskAndRouteMapping TRM2 = 
                        SimpleGreedyAssigner.returnAssignmentMultiPathMinTotalCost(m,n,numPaths,T,MR,f,s,e0, INFINITY);
                MeasuringUtils.reportMultiPathStats(m,n,T,MR,f,s,e0,TRM2.getTaskMapping(),TRM2.getRouteMapping());
                break;
        }
        
        
        
    }
    
    
    /**
     * @param ft The "Feeding" task
     * @param at The "depenedant" task
     * @param nTopo The network
     * @param thisNode the node ID
     * @param aP The ATaG program
     * @param T task allocation matrix
     * @returns the list of nodes that can possibly host copies of at depending
     * on the copy of ft instantiated on node x     */
    private static ArrayList<NodeInfo> nodesWithDependantTasks(ATaGTaskDeclaration ft,
            ATaGTaskDeclaration at, NetworkTopology nTopo, NodeInfo thisNode,
            ATaGProgram aP){
        
        ArrayList<NodeInfo> feedingNodes = new ArrayList();
        
        int regionID = thisNode.getRegionID();
        
        for (ATaGDataItemDeclaration ad:
            aP.getOutDataListOfTask(ft.getID())){
                if (aP.getOutTasksListOfData(ad.getID()).contains(at)){
                    //get outChannel
                    ScopedChannel aoc =
                            aP.getChannelFromTaskAndData(ft.getID(),ad.getID());
                    
                    
                    String aocInterest = aoc.getInterest();
                    if(aocInterest.equalsIgnoreCase("domain")){
                        //this is domain.. can only be paired with a local
                        //make a list of
                        //all nodes in this region
                        for (NodeInfo tempn: nTopo
                                .getNodesInRegion(ft.getMyRegionName(), regionID)){
                            //add all nodes in region r with the feeder task
//                            if (tempn.isTaskAssigned(ft.getID()))
                            feedingNodes.add(tempn);
                        }
                        break;
                    }else if (aocInterest.equalsIgnoreCase("region-hops")){
                        //this is region-hops. make a list of nodes + 1 InterestParam
                        int regionDelta = Integer.parseInt(aoc.getInterestParam());
                        for (int r = Math.max(0,regionID - regionDelta); r<= regionID+regionDelta; r++){
                            for (NodeInfo tempn: nTopo.getNodesInRegion(ft.getMyRegionName(), r)){
                                //add all nodes in region r with the feeder task
                                //                              if (tempn.isTaskAssigned(ft.getID()))
                                feedingNodes.add(tempn);
                            }
                        }
                        break;
                    }
                    
                    //get inChannel
                    ScopedChannel aic = aP.getChannelFromTaskAndData(at.getID(),ad.getID());
                    String aicInterest = aic.getInterest();
                    if(aicInterest.equalsIgnoreCase("domain")){
                        //this is domain.. make a list of
                        //all nodes in this region
                        for (NodeInfo tempn: nTopo
                                .getNodesInRegion(at.getMyRegionName(), regionID)){
                            //add all nodes in region r with the feeder task
                            //                        if (tempn.isTaskAssigned(ft.getID()))
                            feedingNodes.add(tempn);
                        }
                        break;
                    }else if (aicInterest.equalsIgnoreCase("region-hops")){
                        //this is region-hops. make a list of nodes + 1 InterestParam
                        int regionDelta = Integer.parseInt(aic.getInterestParam());
                        for (int r = Math.max(0,regionID - regionDelta); r<= Math.min(regionID+regionDelta, nTopo.numberOfRegions(at.getMyRegionName())); r++){
                            for (NodeInfo tempn: nTopo.getNodesInRegion(at.getMyRegionName(), r)){
                                //add all nodes in region r with the feeder task
                                //                          if (tempn.isTaskAssigned(ft.getID()))
                                feedingNodes.add(tempn);
                            }
                        }
                        break;
                    }
                }
                
            }
            return feedingNodes;
    }
}